package encheres;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

import encheres.ihm.FenetreCommissaire;
import encheres.ihm.ListeNoireModifier;
import encheres.ihm.VueDefinitionPrix;
import exception.NTWException;
import reseau.session.client.SessionG_Client;
import reseau.session.client.SessionG_TCP_Client;
import util.XML;

/**
 * Commissaire Priseur de l'application "Encheres"
 */
public class CommissairePriseur
{
	/**
	 * Couche session
	 */
	private SessionG_Client session;
	
	/**
	 * Thread interne pour la reception des messages
	 */
	private InternalThread thread;
	
	/**
	 * Liste noire des clients non autorises a participer aux encheres
	 */
	private ArrayList<String> listeNoire;
	
	/**
	 * Liste des clients exclus (qui ne peuvent pas encherir)
	 */
	private ArrayList<String> listeExclus;
	
	/**
	 * IHM (vue)
	 */
	private FenetreCommissaire vue;
	
	/**
	 * Objets en vente (clef == id de l'objet)
	 */
	private HashMap<Integer, HashMap> objetsEnVente;
	
	/**
	 * Objets disponibles (clef == id de l'objet)
	 */
	private HashMap<Integer, HashMap> objetsDisponibles;
	
	/**
	 * Objet de l'enchere courante
	 */
	private HashMap objetCourant;
	
	/**
	 * Prix courant de l'enchere
	 */
	private long prixCourant;
	
	/**
	 * Client vainqueur courant de l'enchere
	 */
	private String vainqueurCourant;
	
	/**
	 * Bonus / Malus des clients
	 */
	private HashMap<String, Long> scoreClients;
	
	/**
	 * Ptite fenetre de definition des prix des objets
	 */
	private VueDefinitionPrix vuePrix;
	
	/**
	 * Constructeur par defaut
	 * @throws NTWException si probleme reseau
	 */
	public CommissairePriseur(String ip, int port) throws NTWException
	{
		session = new SessionG_TCP_Client("commissaire", ip, port);
		thread = new InternalThread();
		thread.start();
		
		scoreClients = new HashMap<String, Long>();
		listeNoire = new ArrayList<String>();
		objetsEnVente = new HashMap<Integer, HashMap>();
		objetsDisponibles = new HashMap<Integer, HashMap>();
		
		vue = new FenetreCommissaire();
		vuePrix = new VueDefinitionPrix();
		vuePrix.getButtonValider().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				synchronized (vuePrix) {
					vuePrix.notifyAll();
				}
			}
		});
		
		// *** Remplissage de la liste des objets disponibles
		objetsDisponibles = (HashMap<Integer, HashMap>) XML.load("Data/listeObjets");
		for (HashMap obj : objetsDisponibles.values())
		{
			vue.getListeObjetsDisponibles().addObjet(obj, null, null);
		}
		
		// *** Traitement du bouton "Ajouter" de la liste des objets
		vue.getBtnAjouter().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				
				(new Thread(){
					public void run() {

						HashMap obj = vue.getListeObjetsDisponibles().getSelectedObject();
						if (obj == null ) return;
						
						vuePrix.setVisible(true);
						
						synchronized (vuePrix) {
							try {
								vuePrix.wait();
							} catch (InterruptedException e1){
								e1.printStackTrace();
							}
						}
						
						obj.put("prixDepart", vuePrix.getPrixDep());
						obj.put("prixReserve", vuePrix.getPrixRes());
						obj.put("prixAssurance", vuePrix.getPrixAss());
						
						vuePrix.setVisible(false);
						
						vue.getListeObjetsEnVente().addObjet(obj, null, null);
						vue.getListeObjetsDisponibles().removeObjet(obj);
						vue.pack();
						objetsEnVente.put((Integer) obj.get("id"), obj);
					}
				}).start();
			}
		});
		
		// *** Traitement du bouton "Retirer" de la liste des objets
		vue.getBtnRetirer().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				HashMap obj = vue.getListeObjetsEnVente().getSelectedObject();
				if (obj == null ) return;
				vue.getListeObjetsDisponibles().addObjet(obj, null, null);
				vue.getListeObjetsEnVente().removeObjet(obj);
				vue.pack();
				objetsEnVente.remove((Integer) obj.get("id"));
			}
		});
		
		// *** Traitement du bouton "Demarrer les encheres"
		vue.getBtnDemarrerEnchere().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e) {
				HashMap obj = vue.getListeObjetsEnVente().getSelectedObject();
				if (obj == null ) return;
				demarrerEnchere((Integer) obj.get("id"));
			}
		});
		
		// *** Traitement du bouton "Envoyer la liste des objets"
		vue.getBtnEnvoyerListeObjets().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				listeObjets();
			}
		});
		
		// *** Traitement du bouton "Demarrer les ventes"
		vue.getBtnDemarrerVentes().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				demarrerVentes();
			}
		});
		
		// *** Traitement du bouton "Finir les ventes"
		vue.getBtnFinVentes().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				finVentes();
			}
		});
		
		// *** Traitement du bouton "Finir l'enchere courante"
		vue.getBtnFinEnchereCourante().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				finEnchereCourante();
			}
		});
		
		// *** Traitement du bouton "Modifier la liste noire"
		// TODO : pour l'instant bug
		vue.getBtnModifierListeNoire().setVisible(false);
		vue.getBtnModifierListeNoire().addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent e){
				new ListeNoireModifier();
			}
		});
		
		objetCourant = null;
		prixCourant = 0;
		listeExclus = new ArrayList<String>();
		
		// *** Lire le fichier de liste noire
		try{
		String ligne;
		InputStream ips=new FileInputStream("listeNoire.txt");  
		InputStreamReader ipsr=new InputStreamReader(ips);
		BufferedReader br=new BufferedReader(ipsr);
		
		while((ligne=br.readLine())!=null){listeNoire.add(ligne);}
		}
		catch(Exception e){
			System.out.println("Erreur de lecture\n");
		}
		
		vue.getBtnFinEnchereCourante().setEnabled(false);
		vue.getBtnFinVentes().setEnabled(false);
		vue.getBtnDemarrerEnchere().setEnabled(false);
		
		vue.setVisible(true);
	}
	
	/**
	 * Traite un message entrant
	 */
	private void traiterMessage(HashMap<String, Object> mes)
	{
		System.out.println("Recu : " + mes);
		System.out.println("Enchere courante : " + prixCourant);
		System.out.println("Vainqueur courant : " + vainqueurCourant);
		
		// recupere la commande du message
		String cmd = (String) mes.get("cmd");
		
		// si c'est une declaration d'un client
		if (cmd.equals("proposerClient"))
		{
			proposerClient((String) mes.get("nom"));
		}
		
		// si c'est la bougie qui est consummee
		if (cmd.equals("bougieConsumee"))
		{
			bougieConsumee();
		}
		
		// si un client propose un prix
		if (cmd.equals("proposerPrixCourant"))
		{
			proposerPrixCourant((String) mes.get("nom"), (Long) mes.get("prix"));
		}
		
		// demande si le prix final est OK
		if (cmd.equals("proposerPrixFinal"))
		{
			proposerPrixFinal((String) mes.get("nom"), (Long) mes.get("prix"));
		}
	}
	
	/**
	 * Un client propose un prix final
	 */
	private void proposerPrixFinal(String pseudo, long prix)
	{
		long prixReserve = (Long) objetCourant.get("prixReserve");
		long prixAssurance = (Long) objetCourant.get("prixAssurance");
		if (prix > prixReserve) {
			validerPrixFinal(prix, true);
			long difference = prixAssurance - prix;
			changeScoreClient(difference, pseudo);
		}
		else validerPrixFinal(prix, false);
	}

	/**
	 * Un client propose un prix courant
	 */
	private void proposerPrixCourant(String pseudo, long prix)
	{
		if (listeExclus.contains(pseudo)) validerPrixCourant(pseudo, prix, false);
		else if (prix > prixCourant) validerPrixCourant(pseudo, prix, true);
		else validerPrixCourant(pseudo, prix, false);
	}

	/**
	 * La bougie est consumee
	 */
	private void bougieConsumee()
	{
		finEnchereCourante();
	}

	/**
	 * Refuse un client
	 */
	private void refuserClient(String pseudo)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "accepter");
		mes.put("nom", pseudo);
		mes.put("accepter", false);
		session.deposerMessage(mes);
	}
	
	/**
	 * Accepte un client
	 */
	private void accepterClient(final String pseudo)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "accepter");
		mes.put("nom", pseudo);
		mes.put("accepter", true);
		session.deposerMessage(mes);
		
		vue.getListeClients().addClient(pseudo, new ActionListener(){
			public void actionPerformed(ActionEvent arg0) {
				exclureClient(pseudo);
			}
		}, new ActionListener(){
			public void actionPerformed(ActionEvent arg0) {
				reinclureClient(pseudo);
			}
		});
		vue.pack();
	}

	/**
	 * Traite le cas d'un client qui se declare
	 */
	private void proposerClient(String pseudo)
	{
		// si le pseudo fait parti de la liste noire, on envoit un message de refus
		if (listeNoire.contains(pseudo)) refuserClient(pseudo);
		
		// sinon, on envoit un message d'acceptation et on l'ajoute a la liste
		else accepterClient(pseudo);
	}
	
	/**
	 * Exclut un client
	 */
	private void exclureClient(String pseudo)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "exclure");
		mes.put("nom", pseudo);
		session.deposerMessage(mes);
		listeExclus.add(pseudo);
	}
	
	/**
	 * Reinclut un client
	 */
	private void reinclureClient(String pseudo)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "reinclure");
		mes.put("nom", pseudo);
		session.deposerMessage(mes);
		listeExclus.remove(pseudo);
	}
	
	/**
	 * Termine l'enchere courante
	 */
	private void finEnchereCourante()
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "finEnchereCourante");
		session.deposerMessage(mes);
		vue.getBtnDemarrerEnchere().setEnabled(true);
		vue.getBtnFinEnchereCourante().setEnabled(false);
		vue.getBtnFinVentes().setEnabled(true);
		// TODO : si tous les objets sont vendus, fin des ventes.
		System.out.println(scoreClients);
	}
	
	/**
	 * Termine les ventes
	 */
	private void finVentes()
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "finVentes");
		session.deposerMessage(mes);
		vue.getBtnAjouter().setEnabled(true);
		vue.getBtnRetirer().setEnabled(true);
		vue.getBtnDemarrerVentes().setEnabled(true);
		vue.getBtnFinVentes().setEnabled(false);
		vue.getBtnDemarrerEnchere().setEnabled(false);
	}
	
	/**
	 * Valide le prix courant propose
	 */
	private void validerPrixCourant(String pseudo, long prix, boolean valider)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "validerPrixCourant");
		mes.put("nom", pseudo);
		mes.put("prix", prix);
		mes.put("valider", valider);
		session.deposerMessage(mes);
		if (valider) {
			prixCourant = prix;
			vainqueurCourant = pseudo;
		}
	}
	
	/**
	 * Valide le prix final propose
	 */
	private void validerPrixFinal(long prix, boolean valider)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "validerPrixFinal");
		mes.put("prix", prix);
		mes.put("valider", valider);
		session.deposerMessage(mes);
	}
	
	/**
	 * Demarre les ventes
	 */
	private void demarrerVentes()
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "demarrerVentes");
		session.deposerMessage(mes);
		vue.getBtnAjouter().setEnabled(false);
		vue.getBtnRetirer().setEnabled(false);
		vue.getBtnDemarrerVentes().setEnabled(false);
		vue.getBtnFinVentes().setEnabled(true);
		vue.getBtnDemarrerEnchere().setEnabled(true);
	}
	
	/**
	 * Demarre l'enchere avec l'objet definit par un id
	 */
	private void demarrerEnchere(int id)
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "demarrerEnchere");
		mes.put("id", id);
		session.deposerMessage(mes);
		objetCourant = objetsEnVente.get(id);
		prixCourant = (Long) objetsEnVente.get(id).get("prixDepart");
		vainqueurCourant = null;
		vue.getBtnDemarrerEnchere().setEnabled(false);
		vue.getBtnFinEnchereCourante().setEnabled(true);
		vue.getBtnFinVentes().setEnabled(false);
	}
	
	/**
	 * Envoit la liste des objets proposes
	 */
	private void listeObjets()
	{
		HashMap mes = new HashMap();
		mes.put("cmd", "listeObjets");
		mes.put("liste", new ArrayList<HashMap>(objetsEnVente.values()));
		session.deposerMessage(mes);
	}
	
	/**
	 * Donne un malus a un client
	 */
	private void changeScoreClient(long difference, String client)
	{
		System.out.println(difference + " - " + client);
		if (scoreClients.containsKey(client))
		{
			long current = scoreClients.get(client);
			current += difference;
			scoreClients.put(client, current);
		}
		else
		{
			scoreClients.put(client, difference);
		}
		System.out.println(scoreClients);
	}
	
	/**
	 * Traite interne du traitement des messages
	 */
	private class InternalThread extends Thread
	{
		public void run()
		{
			while(true)
			{
				session.attendreMessage();
				HashMap<String, Object> mess = session.retirerMessage();
				traiterMessage(mess);
			}
		}
	}

}
